#include "Engine.h"
#include <set>
#include <fstream>
#include <algorithm>
#include <cmath>
#include <ctime>
#include "CommonFuc.h"
#include "service_log.hpp"
using namespace std;
using namespace COMMON;
Engine* Engine::m_pInstance = NULL;

bool cmpRankInfo(const RankInfo& r1, const RankInfo& r2)
{
    return r1.score > r2.score;
}

int Engine::loadForwardIndex(const string& file_name)
{
    ifstream fin(file_name.c_str());
    if(!fin)
    {
        _ERROR("in Engine::loadForwardIndex(), file_name:\"%s\"", file_name.c_str());
        return 0;
    }

    string line = "";
    while(getline(fin, line))
    {
        vector<string> v;
        m_st.split(line, "\t", v);
        if(v.size() != 3)
        {
            _INFO("in Engine::loadForwardIndex(), line format error, line:\"%s\"", line.c_str());
            continue;
        }

        if(v[2].size() <= 0)
            continue;

        _id_infostr_map[v[0]] = v[1];
        //cout << v[0] << v[1] << endl;
    }
    return _id_infostr_map.size();
}

int Engine::loadInvertedIndex(const string& file_name)
{
    ifstream fin(file_name.c_str());
    if(!fin)
    {
        _ERROR("in Engine::loadInvertedIndex(), file_name:\"%s\"", file_name.c_str());
        return 0;
    }

    string line = "";
    while(getline(fin, line))
    {
        vector<string> v;
        m_st.split(line, "\t", v);
        if(v.size() < 2)
        {
            _INFO("in Engine::loadForwardIndex(), line format error, line:\"%s\"", line.c_str());
            continue;
        }

        string key = v[0];
        for(int i = 1; i < v.size(); ++i)
        {
            _key_index_map[key].push_back(v[i]);
        }

        //cout << key << " : " << _key_index_map[key].size() << endl;
    }

    return _key_index_map.size();
}

bool Engine::Init(const string& fwIndex_file, const string& ivIndex_file)
{
    int fw_num = loadForwardIndex(fwIndex_file);
    int iv_num = loadInvertedIndex(ivIndex_file);
    _INFO("fwIndex.size = %d, ivIndex.size = %d", fw_num, iv_num);
    if(fw_num <= 0 || iv_num <= 0)
    {
        _ERROR("in Engine::Init()");
        return false;
    }
    return true;
}

double Engine::keywordWeighing(const std::string& key, const double& idf)
{
    return idf;
}

std::vector<std::string>* Engine::getIndexpByKey(const string& key)
{
    if(_key_index_map.find(key) != _key_index_map.end())
        return &(_key_index_map[key]);
    else
        return NULL;
}

int Engine::getIndexpMapByKeyVec(std::vector<std::string>& key_vec, IndexpMap& key_indexp_map)
{
    key_indexp_map.clear();
    for(int i = 0; i < key_vec.size(); ++i)
    {
        string key = key_vec[i];
        cout << key << endl;
        std::vector<std::string>* indexp = getIndexpByKey(key);
        if(indexp == NULL)
            continue;
        else
            key_indexp_map[key] = indexp;
    }

    //for(IndexpMap::iterator it = key_indexp_map.begin(); it != key_indexp_map.end(); ++it)
    //    cout << it->first << "\t" << it->second->size() << endl;

    return key_indexp_map.size();
}

int Engine::countInterset(IndexpMap& key_indexp_map, std::vector<RankInfo>& rankinfo_vec)
{
    rankinfo_vec.clear();
    if(key_indexp_map.size() == 0)
        return 0;

    vector<string> key_vec;
    for(IndexpMap::iterator it = key_indexp_map.begin(); it != key_indexp_map.end(); ++it)
    {
        //cout << it->first << endl;
        if(key_vec.size() == 0)
        {
            key_vec.push_back(it->first);
            continue;
        }

        bool insertFlag = false;
        for(vector<string>::iterator jt = key_vec.begin(); jt != key_vec.end(); ++jt)
        {
            if((it->second)->size() < key_indexp_map[*jt]->size())
            {
                key_vec.insert(jt, it->first);
                insertFlag = true;
                break;
            }
        }
        if(!insertFlag)
            key_vec.push_back(it->first);
    }

    tr1::unordered_map<string, RankInfo> id_rankinfo_map;
    int max_shotnum = 0;
    int total_count = 0;
    for(int i = 0; i < key_vec.size(); ++i)
    {
        //cout << key_vec[i] << " : " << key_indexp_map[key_vec[i]]->size() << endl;
        string key = key_vec[i];
        vector<string>* indexp = key_indexp_map[key];
        for(int j = 0; j < indexp->size(); ++j)
        {
            total_count += 1;
            if(total_count > 10000)
                break;
            string id = (*indexp)[j];
            id_rankinfo_map[id].id = id;
            // here, the score is only considered match
            id_rankinfo_map[id].score += keywordWeighing( key, 1.0 / ((*indexp).size() + 0.000001) );
            //id_rankinfo_map[id].shot_num += 1;
            //id_rankinfo_map[id].shotkey_weight_map[key] += 1;

            //if(id_rankinfo_map[id].shot_num > max_shotnum)
            //    max_shotnum = id_rankinfo_map[id].shot_num;
        }
    }

    for(tr1::unordered_map<string, RankInfo>::iterator it = id_rankinfo_map.begin(); it != id_rankinfo_map.end(); ++it)
    {
        rankinfo_vec.push_back(it->second);
    }
    return rankinfo_vec.size();
}


int Engine::rank(vector<string> key_vec, vector<RankInfo>& rankinfo_vec)
{
    sort(rankinfo_vec.begin(), rankinfo_vec.end(), cmpRankInfo);
    std::vector<RankInfo> tmp_rankinfo_vec;
    for(int i = 0; i < rankinfo_vec.size() && i < 300; ++i)
    {
        tmp_rankinfo_vec.push_back(rankinfo_vec[i]);
    }
    rankinfo_vec = tmp_rankinfo_vec;
    tmp_rankinfo_vec.clear();

    for(int i = 0; i < rankinfo_vec.size(); ++i)
    {
        string id = rankinfo_vec[i].id;
        string infostr = _id_infostr_map[id];

        // cover
        int shotnum = 0;
        vector<string> str_vec;
        m_seg.gbksegment2unigram(infostr, str_vec);
        vector<int> pos_vec;
        for(int j = 0; j < str_vec.size(); ++j)
        {
            if(find(key_vec.begin(), key_vec.end(), str_vec[j]) != key_vec.end())
            {
                pos_vec.push_back(j);
                shotnum += 1;
            }
        }    
        double cover = shotnum / (double)(str_vec.size() + 0.000001);

        // match
        double match = rankinfo_vec[i].score;
        match *= shotnum;

        // compactness
        double compactness = 1.0;
        
        //if(pos_vec.size() > 1)
        //{
        //    int pre_pos = pos_vec[0];
        //    for(int j = 1; j < pos_vec.size(); ++j)
        //    {
        //        compactness *= ((pos_vec[j] - pre_pos));
        //        pre_pos = pos_vec[j];
        //    }
        //}
        //compactness = 1.0 / compactness;
        
        rankinfo_vec[i].score = pow(match, 2) * cover * pow(compactness, 0.5);
        //cout << "infostr = "<< infostr << endl << "shot = " << shotnum << "\tmatch = " << match << "\tcover = " << cover << "\tcompactness = " << compactness << "\tscore = " << rankinfo_vec[i].score << endl;
    }
    sort(rankinfo_vec.begin(), rankinfo_vec.end(), cmpRankInfo);

    return rankinfo_vec.size();
}

bool Engine::formatRst(std::vector<RankInfo>& rankinfo_vec)
{
    for(int i = 0; i < rankinfo_vec.size() && i < 10; ++i)
    {
        cout << rankinfo_vec[i].id << "=" << rankinfo_vec[i].score << endl;
        cout << _id_question_map[rankinfo_vec[i].id] << endl;
        cout << endl;
    }
}


bool Engine::Search(string& query, vector<Rst>& rst_vec, const int& ret_num)
{
    if(query.size() > 200)
    {
        _ERROR("[input query is too long, query.size() = %d > 200]", query.size());
        return false;
    }
    
    vector<string> key_vec1, key_vec2;
    m_seg.segment2unigram(query, key_vec1);
    m_seg.unigram2bigram(key_vec1, key_vec2);

    IndexpMap key_indexp_map;
    IndexpMap key_indexp_map1;
    IndexpMap key_indexp_map2;

    int key_indexp_ret1 = getIndexpMapByKeyVec(key_vec1, key_indexp_map1);
    int key_indexp_ret2 = getIndexpMapByKeyVec(key_vec2, key_indexp_map2);
    if(key_indexp_ret1 + key_indexp_ret2 <= 0)
    {
        _ERROR("in Search(), getIndexpMapByKeyVec(), failed");
        return false;
    }
    string bigram_str = "";
    for(IndexpMap::iterator it = key_indexp_map2.begin(); it != key_indexp_map2.end(); ++it)
    {
        //cout << "key_indexp_map2:\t" << it->first << it->second->size() << endl;
        key_indexp_map[it->first] = it->second;
        bigram_str += it->first;
    }
    for(IndexpMap::iterator it = key_indexp_map1.begin(); it != key_indexp_map1.end(); ++it)
    {
        //cout << "key_indexp_map1:\t" << it->first << it->second->size() << endl;
        if(bigram_str.find(it->first) != string::npos)
            continue;
        else
            key_indexp_map[it->first] = it->second;
    }

    vector<RankInfo> rankinfo_vec;
    if(countInterset(key_indexp_map, rankinfo_vec) <= 0)
    {
        _ERROR("in Search(), countInterset(), failed");
        return false;
    }

    
	if(rank(key_vec1, rankinfo_vec) <= 0)
    {
        _ERROR("in Search(), rank(), failed");
        return false;
    }

    set<string> infostr_set;
	for(int i = 0; i < rankinfo_vec.size(); ++i)
    {
        Rst rst;
        
        rst.infostr = _id_infostr_map[rankinfo_vec[i].id];

        if(infostr_set.find(rst.infostr) != infostr_set.end())
            continue;
        if(infostr_set.size() > ret_num)
            break;
        infostr_set.insert(rst.infostr);
        rst_vec.push_back(rst);
    }
}
